#include "axistiledmapscene.h"
#include "axissprite.h"
#include <QDebug>

const int g_MapWidth    = 8;
const int g_MapHeight   = 10;
const int g_GridWidth   = 64;
const int g_GridHeight  = 32;

AxisTiledMapScene::AxisTiledMapScene(QObject *parent)
    : QGraphicsScene(parent)
{
    setBackgroundBrush(Qt::darkGray);
    _map.initMap(g_MapWidth, g_MapHeight, g_GridWidth, g_GridHeight);

    addText("(0, 0)");
    const double pxw = (_map.getMapHeight() / 2.0 * _map.getGridWidth())
            + (_map.getMapWidth() / 2.0 * _map.getGridWidth());
    const double pxh = (_map.getMapHeight() / 2.0 * _map.getGridHeight())
            + (_map.getMapWidth() / 2.0 * _map.getGridHeight());;
    setSize(pxw, pxh);
}

void AxisTiledMapScene::addSprite(AxisSprite *sp)
{
    sp->setScene(this);
    _sprites.append(sp);
}

void AxisTiledMapScene::setMousePos(QPoint pos)
{

}

void AxisTiledMapScene::setSize(int w, int h)
{
    _width = w;
    _height = h;
}

QPolygonF AxisTiledMapScene::calPolygonPath(int x, int y)
{
    QPolygonF pointlist;
    pointlist.append(QPoint(x, y + _map.getGridHeight() / 2));
    pointlist.append(QPoint(x + _map.getGridWidth() / 2, y + _map.getGridHeight()));
    pointlist.append(QPoint(x + _map.getGridWidth(), y + _map.getGridHeight() / 2));
    pointlist.append(QPoint(x + _map.getGridWidth() / 2, y));

    return pointlist;
}

void AxisTiledMapScene::drawMousePos(QPainter *painter, int w, int h)
{
    const int offsetw = w / 2;
    const int offseth = h / 2;
    const int ogridx = _tilex - offsetw;
    const int ogridy = _tiley - offseth;
    for ( int hang = 0; hang < h; ++hang )
    {
        for ( int lie = 0; lie < w; ++lie )
        {
            QPointF scpos = tileToScreenCoords(ogridx + lie, ogridy + hang);
            QPolygonF polygon;
            polygon << scpos
                    << QPointF(scpos.x() - _map.getGridWidth()/2,
                    scpos.y() + _map.getGridHeight()/2)
                    << QPointF(scpos.x(), scpos.y() + _map.getGridHeight())
                    << QPointF(scpos.x() + _map.getGridWidth()/2,
                             scpos.y() + _map.getGridHeight()/2);

            if ( _map.isValidGrid(ogridx + lie, ogridy + hang))
            {
                painter->setBrush(QColor(17,127,229));
            }
            else
            {
                painter->setBrush(QColor(224,103,126));
            }
            painter->drawPolygon(polygon);
        }
    }
}

void AxisTiledMapScene::drawGrid(QPainter *painter, const QRectF &rect, QColor gridColor) const
{
    const int tileWidth = _map.getGridWidth();
    const int tileHeight= _map.getGridHeight();

    QRect r = rect.toAlignedRect();
    r.adjust(-tileWidth / 2, -tileHeight / 2,
             tileWidth / 2, tileHeight / 2);

    ;
    const int startx = qMax(qreal(0), screenToTileCoords(r.topLeft()).x());
    const int starty = qMax(qreal(0), screenToTileCoords(r.topRight()).y());
    const int endx = qMin(qreal(_map.getMapWidth()),
                          screenToTileCoords(r.bottomRight()).x());
    const int endy = qMin(qreal(_map.getMapHeight()),
                          screenToTileCoords(r.bottomLeft()).y());

    painter->setPen(Qt::black);

    for ( int y = starty; y <= endy; ++y )
    {
        const QPointF start = tileToScreenCoords(startx, y);
        const QPointF end = tileToScreenCoords(endx, y);
        painter->drawLine(start, end);
    }
    for ( int x = startx; x <= endx; ++x )
    {
        const QPointF start = tileToScreenCoords(x, starty);
        const QPointF end = tileToScreenCoords(x, endy);
        painter->drawLine(start, end);
    }
}

void AxisTiledMapScene::drawGridSelection(QPainter *painter,
                                          const QRegion &region,
                                          const QColor &color,
                                          const QRectF &exposed) const
{
    painter->setBrush(color);
    painter->setPen(Qt::NoPen);
    for(const QRect& r:region.rects())
    {
        QPolygonF polygon = tileRectToScreenPolygon(r);
        if( QRectF(polygon.boundingRect()).intersects(exposed) )
        {
            painter->drawConvexPolygon(polygon);
        }
    }
}

QPointF AxisTiledMapScene::screenToTileCoords(QPointF p) const
{
    return screenToTileCoords(p.x(), p.y());
}

QPointF AxisTiledMapScene::screenToTileCoords(qreal x, qreal y) const
{
    const int tilewidth = _map.getGridWidth();
    const int tileheight= _map.getGridHeight();

    x -= _map.getMapHeight() * tilewidth / 2;
    const qreal tiley = y / tileheight;
    const qreal tilex = x / tilewidth;

    return QPointF(tiley + tilex, tiley - tilex);
}

QPointF AxisTiledMapScene::tileToScreenCoords(qreal x, qreal y) const
{
    const int tilewidth = _map.getGridWidth();
    const int tileheight= _map.getGridHeight();
    const int originx = _map.getMapHeight() * tilewidth / 2;

    return QPointF((x -y) * tilewidth / 2 + originx,
                   (x + y) * tileheight / 2);
}

QPointF AxisTiledMapScene::tileToScreenCoords(QPointF p) const
{
    return tileToScreenCoords(p.x(), p.y());
}

QPointF AxisTiledMapScene::pixelToScreenCoords(qreal x, qreal y) const
{
    const int tilewidth = _map.getGridWidth();
    const int tileheight= _map.getGridHeight();
    const int originx = _map.getMapHeight() * tilewidth / 2;
    const qreal tiley = y / tileheight;
    const qreal tilex = x / tileheight;

    return QPointF((tilex - tiley) * tilewidth / 2 + originx,
                   (tilex + tiley) * tileheight / 2);
}

QPolygonF AxisTiledMapScene::tileRectToScreenPolygon(const QRect &rect) const
{
    const int tilewidth = _map.getMapWidth();
    const int tileheight = _map.getMapHeight();

    const QPointF topright = tileToScreenCoords(rect.topRight());
    const QPointF bottomright = tileToScreenCoords(rect.bottomRight());
    const QPointF bottomleft = tileToScreenCoords(rect.bottomLeft());

    QPolygonF polygon;
    polygon << QPointF(tileToScreenCoords(rect.topLeft()))
            << QPointF(topright.x() + tilewidth / 2, topright.y() + tileheight / 2)
            << QPointF(bottomright.x(), bottomright.y() + tileheight)
            << QPointF(bottomleft.x() - tilewidth / 2, bottomleft.y() + tileheight / 2);
    return polygon;
}

void AxisTiledMapScene::drawBackground(QPainter* painter, const QRectF &rect)
{
    QGraphicsScene::drawBackground(painter, rect);

    for ( AxisSprite* sp : _sprites )
    {
        sp->paint(painter);
    }

    painter->setPen(Qt::NoPen);
    drawMousePos(painter, 1, 1);
    for(QPoint p:_path)
    {
        QPointF scpos = tileToScreenCoords(p.x(), p.y());
        QPolygonF polygon;
        polygon << scpos
                << QPointF(scpos.x() - _map.getGridWidth()/2,
                scpos.y() + _map.getGridHeight()/2)
                << QPointF(scpos.x(), scpos.y() + _map.getGridHeight())
                << QPointF(scpos.x() + _map.getGridWidth()/2,
                         scpos.y() + _map.getGridHeight()/2);

        if ( _map.isValidGrid(p.x(), p.y()))
        {
            painter->setBrush(QColor(17,127,229));
        }
        else
        {
            painter->setBrush(QColor(224,103,126));
        }
        painter->drawPolygon(polygon);
    }

}

void AxisTiledMapScene::drawForeground(QPainter* painter, const QRectF &rect)
{
    QPen pen = painter->pen();
    pen.setColor(QColor(124,124,124));
    pen.setStyle(Qt::DotLine);
    painter->setPen(pen);
    drawGrid(painter, rect, Qt::black);

    const double pxw = (_map.getMapHeight() / 2.0 * _map.getGridWidth())
            + (_map.getMapWidth() / 2.0 * _map.getGridWidth());
    const double pxh = (_map.getMapHeight() / 2.0 * _map.getGridHeight())
            + (_map.getMapWidth() / 2.0 * _map.getGridHeight());;
    painter->drawRect(0, 0, pxw, pxh);
}

void AxisTiledMapScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    _lastmousepos = event->scenePos();
    QGraphicsScene::mouseMoveEvent(event);

    const QPointF tileposf = screenToTileCoords(_lastmousepos);
    QPoint tilepos = QPoint(int(std::floor(tileposf.x())), int(std::floor(tileposf.y())));
    if ( _tilex != tilepos.x() || _tiley != tilepos.y() )
    {
        _tilex = tilepos.x();
        _tiley = tilepos.y();
        update();
    }
}

void AxisTiledMapScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mousePressEvent(event);
    update();

    if ( Qt::LeftButton == event->button() )
    {
        QPointF gridpos2 = screenToTileCoords(event->scenePos().x(), event->scenePos().y());
        QPoint gridpos(int(std::floor(gridpos2.x())), int(std::floor(gridpos2.y())));
        for(AxisSprite* sp : _sprites)
        {
            QList<QPoint> area = sp->getArea();
            for(QPoint p : area)
            {
                if ( p == gridpos )
                {
                    if (_selectsprite)
                    {
                        _selectsprite->choose(false);
                        _selectsprite = nullptr;
                    }
                    sp->choose(true);
                    _selectsprite = sp;
                    return;
                }
            }
        }
        if( _selectsprite )
        {
            _selectsprite->choose(false);
            _selectsprite = nullptr;
        }
    }
}

void AxisTiledMapScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mouseReleaseEvent(event);

    update();

    if ( _selectsprite )
    {
        QPointF gridpos2 = screenToTileCoords(event->scenePos().x(), event->scenePos().y());
        QPoint gridpos(int(std::floor(gridpos2.x())), int(std::floor(gridpos2.y())));

        QPoint startpos = _selectsprite->pos();
        Point start(startpos.x(), startpos.y());
        Point end(gridpos.x(), gridpos.y());
        _path.clear();
        for(Point* p:_map.getAstar().GetPath(start,end,false))
        {
            _path.append(QPoint(p->x, p->y));
        }
        qDebug() << _path;
    }
}
